package Net;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;

import Interfaces.EventLoopAcceptCallback;
import Interfaces.EventLoopReadCallback;
import Util.ApiException;
import Util.BindMethod;
import Util.SafeQueue;

public class EventLoop {
	private Selector selector_;
	private EventLoopAcceptCallback acceptCb_;
	private EventLoopReadCallback readCb_;
	private SafeQueue funcs_;
	private long tid_;
	private final long waitTime_ = 10000;
	
	public void setAcceptCb(EventLoopAcceptCallback cb)
	{
		acceptCb_ = cb;
	}
	
	public void setReadCb(EventLoopReadCallback cb)
	{
		readCb_ = cb;
	}
	
	public EventLoop() throws IOException
	{
		selector_ = Selector.open();
		funcs_ = new SafeQueue();
		tid_ = Thread.currentThread().getId();
	}
	
	public void runInLoop(BindMethod bindMethod) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
	{
		if (isInLoop())
			bindMethod.invoke();
		else
			funcs_.put(bindMethod);
	}
	
	public boolean isInLoop()
	{
		return tid_ == Thread.currentThread().getId();
	}
	
	public void loop() throws InterruptedException, IOException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ApiException
	{
		while (true)
		{
			selector_.select(waitTime_);
			Iterator<SelectionKey> itr = selector_.selectedKeys().iterator();
			while (itr.hasNext())
			{
				SelectionKey key = (SelectionKey)itr.next();
				itr.remove();
				handleKey(key);
			}
			doPendingFunc();
		}
	}
	
	private void handleKey(SelectionKey key) throws IOException, ApiException
	{
		if (key.isAcceptable())
		{
			if (acceptCb_!=null)
				acceptCb_.onAccept(key);
		}else if (key.isReadable())
		{
			if (readCb_!=null)
				readCb_.onRead(key);
		}
	}
	
	public Selector getSelector()
	{
		return selector_;
	}
	
	private void doPendingFunc() throws InterruptedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
	{
		while (!funcs_.isEmpty())
		{
			BindMethod bindMethod = (BindMethod) funcs_.get();
			bindMethod.invoke();
		}
	}
}
